'''
多人聊天室 服务端
'''
#coding:uft-8
from socket import socket,AF_INET,SOCK_STREAM
import threading
import wx # wxPython
import time
'''
服务端对象
'''
class SjServer(wx.Frame):
    def __init__(self,client_name):
        # ------------------------------------------窗体绘制开始-------------------------------------
        '''
        id 界面编号
        title 标题
        pos 位置
        size 窗体大小 单位像素
        '''
        wx.Frame.__init__(self,None,id = 1001,title=client_name,pos=wx.DefaultPosition,size=(400,550))
        # 创建面板对象
        pl = wx.Panel(self)
        # 面板里套盒子
        box = wx.BoxSizer(wx.VERTICAL) # 垂直布局

        fg = wx.FlexGridSizer(wx.HSCROLL) # flex弹性盒子布局
        # 按钮
        start_btn = wx.Button(pl,size=(120,30),label="启动服务")
        save_log_btn = wx.Button(pl,size=(120,30),label="保存聊天记录")
        stop_btn = wx.Button(pl,size=(120,30),label="停止服务")
        # 放到弹性盒子
        fg.Add(start_btn,1,wx.TOP|wx.LEFT)
        fg.Add(save_log_btn,1,wx.TOP|wx.CENTER)
        fg.Add(stop_btn,1,wx.TOP|wx.RIGHT)
        # 弹性盒子放到box
        box.Add(fg,1,wx.ALIGN_CENTER)

        # 只读聊天框
        self.show_text=wx.TextCtrl(pl,size=(400,500),style=wx.TE_MULTILINE|wx.TE_READONLY)
        box.Add(self.show_text,1,wx.ALIGN_CENTER)

        # 盒子放到面板
        pl.SetSizer(box)

        # ------------------------------------------窗体绘制完毕-------------------------------------

        #------------------------------------------服务端功能----------------------------------------
        self.isOn = False # 是否启动
        self.host_port = ('',8888)  # '' 代表本机ip
        self.server_socket = socket(AF_INET,SOCK_STREAM) # socket
        self.server_socket.bind(self.host_port)
        self.server_socket.listen(5)
        self.session_thread_dict = {} # k 客户端名称 v 客户端线程

        # 启动服务按钮点击事件绑定
        self.Bind(wx.EVT_BUTTON,self.start_server,start_btn)
        # 绑定保存聊天记录
        self.Bind(wx.EVT_BUTTON,self.save_log,save_log_btn)
        # 绑定断开连接
        self.Bind(wx.EVT_BUTTON,self.stop_server,stop_btn)

    # 启动按钮点击事件
    def start_server(self,event):
        if not self.isOn:
            self.isOn = True
            print("启动服务成功")
            # 创建服务线程设置为守护线程
            main_thread = threading.Thread(target=self.do_work)
            main_thread.daemon = True
            main_thread.start()
        else:
            print("服务已启动")

    # 保存聊天记录
    def save_log(self,e):
        data = self.show_text.GetValue()
        with open('record_log','w',encoding='utf-8') as file:
            file.write(data)
    # 停止服务
    def stop_server(self,e):
        print("服务已停止")
        self.isOn = False

    # 启动服务
    def do_work(self):
        while self.isOn:
            socket_client,socket_addr = self.server_socket.accept() # 监听客户端
            recv_data = socket_client.recv(1024).decode("utf-8") # 上线第一条内容为客户端名称
            # 创建客户端线程
            session_thread = SessionThread(socket_client,recv_data,self)
            # 存储到字典
            self.session_thread_dict[recv_data] = session_thread
            # 启动客户端线程
            session_thread.start()
            # 启动成功后，在大厅展示该客户端已连接
            self.show_info(recv_data,f'服务端通知:{recv_data}进入了聊天室。',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))
        self.server_socket.close() # 退出循环关闭socket
    
    # 在服务端和客户端展示信息
    def show_info(self,data_source,data,date_time):
        send_data = f'{data_source}:{data}\n时间:{date_time}'
        # 只读文本框展示
        self.show_text.AppendText('-'*40+'\n'+send_data+'\n')
        # 发送到每个客户端
        for session in self.session_thread_dict.values():
            if session.isOn:
                session.client_socket.send(send_data.encode('utf-8'))   
                print("发送通知给客户端",session.client_name)
'''
客户端线程对象
'''
class SessionThread(threading.Thread):
    def __init__(self,client_socket,client_name,server):
        threading.Thread.__init__(self)
        self.client_socket = client_socket
        self.client_name = client_name
        self.server = server
        self.isOn = True

    def run(self) -> None:
        print(f'客户端：{self.client_name}连接成功，服务端启动一个线程会话')
        while self.isOn:
            data = self.client_socket.recv(1024).decode('utf-8')
            if(data == 'S-going_to_disconnect-J'):
                self.server.show_info(self.client_name,f'服务端通知:{self.client_name}退出了聊天室。',time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))
                self.isOn = False
            else:
                # 展示客户端发来的信息给公屏
                self.server.show_info(self.client_name,data,time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))
        self.client_socket.close() # 关闭socket
        
if __name__ == '__main__':
    app = wx.App()
    client = SjServer('雪人聊天室服务端')
    client.Show()
    app.MainLoop() # 循环刷新显示